package codechicken.wirelessredstone.core;

import net.minecraft.src.*;

import java.util.*;

public final class RedstoneEther
{
	public static void init()
	{
		SaveManager.config().getTag("core.jammer").useBraces();
	}
	
	public static int pythagorasPow2(BlockCoord node1, BlockCoord node2)
	{
		return (node1.x - node2.x)*(node1.x - node2.x)+
				(node1.y - node2.y)*(node1.y - node2.y)+
				(node1.z - node2.z)*(node1.z - node2.z);
	}

	public static double pythagorasPow2(BlockCoord node, WRVector3 point)
	{
		return (node.x - point.x)*(node.x - point.x)+
				(node.y - point.y)*(node.y - point.y)+
				(node.z - point.z)*(node.z - point.z);
	}

	public static void reload(World newworld)
	{
		if(CoreProxy.isClient())
		{
			return;
		}
		
		int dimension = CoreProxy.getArrayDimension(newworld);
		
		if(!CoreProxy.isSMP() || dimension == 0)
		{
			freqarray = new RedstoneEtherFrequency[numfreqs + 1];
			for(int freq = 1; freq <= numfreqs; freq++)
			{
				freqarray[freq] = new RedstoneEtherFrequency(freq);
			}
			jammedentities = new HashMap<EntityLiving, Integer>();
			receivingdevices = new HashSet<WirelessReceivingDevice>();
		}
		
		if((!CoreProxy.isSMP() || dimension == 0) && !CoreProxy.isClient())
		{
			playerfrequencymap = new HashMap<String, byte[]>();
		}
		
		wirelessloadlist[dimension] = new HashMap<BlockCoord, Integer>();
		jammednodes[dimension] = new TreeMap<BlockCoord, Integer>();
		jammerset[dimension] = new TreeSet<BlockCoord>();
		
		SaveManager.reloadSave(newworld, dimension);
		SaveManager.getInstance(dimension).loadEther();
		
		if(CoreProxy.isSMP() && dimension == 0)
		{
			publicfrequencyend = SaveManager.getInstance(0).getProperty("PublicFrequencies", 100);
			freqinfoloadprogress = new HashMap<String, Integer>();
			SaveManager.getInstance(0).loadFreqInfo();
		}
		
		if(!CoreProxy.isSMP())
		{
			if(SaveManager.getInstance(0) == null)
			{
				SaveManager.reloadSave(newworld, 0);
			}
			
			SaveManager.getInstance(0).loadFreqInfo();
			for(int dim = 0; dim < 2; dim++)
			{
				if(dim == dimension)
				{
					continue;
				}
	
				SaveManager.reloadSave(newworld, dim);
				SaveManager.getInstance(dim).loadEther();
			}
		}
	}

	public static void resetClient()
	{
		freqarray = new RedstoneEtherFrequency[numfreqs + 1];
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			freqarray[freq] = new RedstoneEtherFrequency(freq);
		}
		jammedentities = new HashMap<EntityLiving, Integer>();
		
		jamAllFrequencies(CoreProxy.getPlayer());
		publicfrequencyend = 0;
	}
	
	public static void saveEther(World world)
	{
		int dimension = CoreProxy.getArrayDimension(world);
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			freqarray[freq].saveFreq(dimension);
		}
		SaveManager.getInstance(dimension).removeTrailingSectors();
	}
	
	public static void verifyChunkTransmitters(World world, int chunkx, int chunkz)
	{
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			freqarray[freq].verifyChunkTransmitters(world, chunkx, chunkz);
		}
	}
	
	public static void loadTransmitter(int dimension, int x, int y, int z, int freq)
	{
		freqarray[freq].loadTransmitter(new BlockCoord(x, y, z), dimension);
	}
	
	public static boolean isFreqOn(int freq)
	{
		return freqarray[freq].isOn();
	}

	public static TileEntity getTile(World world, BlockCoord node)
	{
		return world.getBlockTileEntity(node.x, node.y, node.z);
	}

	public static void setTransmitter(World world, int x, int y, int z, int freq, boolean on)
	{
		if(freq == 0)
		{
			return;
		}
		
		BlockCoord node = new BlockCoord(x, y, z);
		int dimension = CoreProxy.getArrayDimension(world);
		
		if(isNodeInAOEofJammer(node, dimension))
		{
			jamNodeSometime(world, node, dimension, freq);
		}
		freqarray[freq].setTransmitter(world, node, dimension, on);
	}

	public static void remTransmitter(World world, int x, int y, int z, int freq)
	{
		if(freq == 0)
		{
			return;
		}
		
		BlockCoord node = new BlockCoord(x, y, z);
		int dimension = CoreProxy.getArrayDimension(world);
		
		jammednodes[dimension].remove(node);
		freqarray[freq].remTransmitter(world, node, dimension);
	}

	public static void addReceiver(World world, int x, int y, int z, int freq)
	{
		if(freq == 0)
		{
			return;
		}
		
		BlockCoord node = new BlockCoord(x, y, z);
		int dimension = CoreProxy.getArrayDimension(world);
		
		if(isNodeInAOEofJammer(node, dimension))
		{
			jamNodeSometime(world, node, dimension, freq);
		}
		freqarray[freq].addReceiver(world, node, dimension);
	}

	public static void remReceiver(World world, int x, int y, int z, int freq)
	{
		if(freq == 0)
		{
			return;
		}
		
		BlockCoord node = new BlockCoord(x, y, z);
		int dimension = CoreProxy.getArrayDimension(world);

		jammednodes[dimension].remove(node);		
		freqarray[freq].remReceiver(world, node, dimension);
	}
	
	public static void addJammer(World world, int x, int y, int z)
	{
		int dimension = CoreProxy.getArrayDimension(world);
		BlockCoord jammer = new BlockCoord(x, y, z);
		
		jammerset[dimension].add(jammer);
		jamNodesInAOEOfJammer(world, jammer, dimension);		
	}

	public static void remJammer(World world, int x, int y, int z)
	{
		jammerset[CoreProxy.getArrayDimension(world)].remove(new BlockCoord(x, y, z));
	}

	public static void addToLoadList(World world, int x, int y, int z, IRedstoneEtherLoad tile)
	{
		int dimension = CoreProxy.getArrayDimension(world);
		if(processingAddittions == dimension)
		{
			backupmap.put(new BlockCoord(x, y, z), tile.getLoadTicks());
		}
		else
		{
			wirelessloadlist[dimension].put(new BlockCoord(x, y, z), tile.getLoadTicks());
		}
	}
	
	public static void tick(World world)
	{
		int dimension = CoreProxy.getArrayDimension(world);
		if(!CoreProxy.isClient())
		{
			processEtherAdditions(world);
			updateJammedNodes(world);
			randomJamTest(world);
			updateJammedEntities(world);
			entityJamTest(world);
		}
		if(CoreProxy.isSMP() && dimension == 0)
		{
			sendFreqInfo();
		}		
	}

	private static void processEtherAdditions(World world)
	{
		int dimension = CoreProxy.getArrayDimension(world);
		processingAddittions = dimension;
		backupmap = new HashMap<BlockCoord, Integer>();
		for(Iterator<BlockCoord> iterator = wirelessloadlist[dimension].keySet().iterator(); iterator.hasNext();)
		{
			BlockCoord node = iterator.next();
			Integer ticksremaining = wirelessloadlist[dimension].get(node);
			
			if(ticksremaining == 0)
			{
				iterator.remove();
				IRedstoneEtherLoad tileentity = (IRedstoneEtherLoad)getTile(world, node);
				if(tileentity != null)
				{
					tileentity.onEtherLoad();
				}
				continue;
			}
			
			wirelessloadlist[dimension].put(node, ticksremaining - 1);
		}
		
		wirelessloadlist[dimension].putAll(backupmap);
		processingAddittions = -1;
	}
	
	private static void updateJammedNodes(World world)
	{
		int dimension = CoreProxy.getArrayDimension(world);
		for(Iterator<BlockCoord> iterator = jammednodes[dimension].keySet().iterator(); iterator.hasNext();)
		{
			BlockCoord node = iterator.next();
			int inactivetime = jammednodes[dimension].get(node);
			inactivetime--;
			
			if(inactivetime == 0 || inactivetime < 0 && inactivetime%jammerrandom == 0)
			{
				ITileWireless tile = (ITileWireless) getTile(world, node);
				if(tile == null)
				{
					iterator.remove();
					continue;
				}
				
				BlockCoord jammer = getClosestJammer(node, dimension);
				ITileJammer jammertile = jammer == null ? null : (ITileJammer)getTile(world, jammer);
				if(jammertile == null)
				{
					iterator.remove();
					tile.unjamTile();
					continue;
				}
				else
				{
					jammertile.jamTile(tile);
				}
			}
			
			if(inactivetime == 0)//so the node doesn't think it's unjammed
			{
				inactivetime = jammertimeout;
			}
			
			jammednodes[dimension].put(node, inactivetime);
		}
	}

	private static void randomJamTest(World world)
	{
		if(world.getWorldTime() % 600 != 0)//30 seconds
		{
			return;
		}
		
		for(int i = 0; i < 2; i++)
		{
			if(jammerset[i] != null)
			{
				for(Iterator<BlockCoord> iterator = jammerset[i].iterator(); iterator.hasNext();)
				{
					jamNodesInAOEOfJammer(world, iterator.next(), i);
				}
			}
		}
	}

	private static void updateJammedEntities(World world)
	{
		int dimension = CoreProxy.getArrayDimension(world);
		for(Iterator<EntityLiving> iterator = jammedentities.keySet().iterator(); iterator.hasNext();)
		{
			EntityLiving entity = iterator.next();
			int inactivetime = jammedentities.get(entity);
			inactivetime--;
			
			if(entity.isDead || entity == null)//logged out or killed
			{
				iterator.remove();
				continue;
			}
			
			if(inactivetime == 0//time for unjam or rejam
					|| (inactivetime < 0 && inactivetime%jammerentitywait == 0)//time to jam from the sometime
					|| (inactivetime > 0 && inactivetime%jammerentityretry == 0))//send another bolt after the retry time
			{
				BlockCoord jammer = getClosestJammer(new WRVector3(entity), dimension);
				ITileJammer jammertile = jammer == null ? null : (ITileJammer)getTile(world, jammer);
				if(jammertile == null)
				{
					if(inactivetime <= 0)//not a rejam test
					{
						iterator.remove();
						jamEntity(entity, false);
						continue;
					}
				}
				else
				{
					jammertile.jamEntity(entity);
				}
			}
			
			if(inactivetime == 0)//so the node doesn't think it's unjammed
			{
				inactivetime = jammertimeout;
			}
			
			jammedentities.put(entity, inactivetime);
		}
	}

	private static void entityJamTest(World world)
	{
		if(world.getWorldTime() % 10 != 0)
		{
			return;
		}
		
		int dimension = CoreProxy.getArrayDimension(world);
		for(Iterator<BlockCoord> iterator = jammerset[dimension].iterator(); iterator.hasNext();)
		{
			BlockCoord jammer = iterator.next();
			@SuppressWarnings("unchecked")
			List<Entity> entitiesinrange = world.getEntitiesWithinAABBExcludingEntity(null, AxisAlignedBB.getBoundingBox(jammer.x-9.5, jammer.y-9.5, jammer.z-9.5, jammer.x+10.5, jammer.y+10.5, jammer.z+10.5));
			for(Iterator<Entity> iterator2 = entitiesinrange.iterator(); iterator2.hasNext();)
			{
				Entity entity = iterator2.next();
				if(!(entity instanceof EntityLiving))
				{
					continue;
				}
				if(entity instanceof EntityPlayer)
				{
					if(isPlayerJammed((EntityPlayer)entity))
					{
						continue;
					}
				}
				jamEntitySometime((EntityLiving) entity);
			}
		}
	}

	public static boolean isNodeJammed(World world, int x, int y, int z)
	{
		Integer timeout = jammednodes[CoreProxy.getArrayDimension(world)].get(new BlockCoord(x, y, z));
		return timeout != null && timeout > 0;
	}

	public static boolean isPlayerJammed(EntityPlayer player)
	{
		Integer timeout = jammedentities.get(player);
		return timeout != null && timeout > 0;
	}

	public static boolean isNodeInAOEofJammer(BlockCoord node, int dimension)
	{
		for(Iterator<BlockCoord> iterator = jammerset[dimension].iterator(); iterator.hasNext();)
		{
			BlockCoord jammer = iterator.next();
			if(pythagorasPow2(jammer, node) < jammerrangePow2)
			{
				return true;
			}
		}
		return false;
	}

	public static boolean isPointInAOEofJammer(WRVector3 point, int dimension)
	{
		for(Iterator<BlockCoord> iterator = jammerset[dimension].iterator(); iterator.hasNext();)
		{
			BlockCoord jammer = iterator.next();
			if(pythagorasPow2(jammer, point) < jammerrangePow2)
			{
				return true;
			}
		}
		return false;
	}

	public static BlockCoord getClosestJammer(BlockCoord node, int dimension)
	{
		BlockCoord closestjammer = null;
		double closestdist = jammerrangePow2;
		for(Iterator<BlockCoord> iterator = jammerset[dimension].iterator(); iterator.hasNext();)
		{
			BlockCoord jammer = iterator.next();
			double distance = pythagorasPow2(jammer, node);
			if(distance < closestdist)
			{
				closestjammer = jammer;
				closestdist = distance;
			}
		}
		return closestjammer;
	}

	public static BlockCoord getClosestJammer(WRVector3 point, int dimension)
	{
		BlockCoord closestjammer = null;
		double closestdist = jammerrangePow2;
		for(Iterator<BlockCoord> iterator = jammerset[dimension].iterator(); iterator.hasNext();)
		{
			BlockCoord jammer = iterator.next();
			double distance = pythagorasPow2(jammer, point);
			if(distance < closestdist)
			{
				closestjammer = jammer;
				closestdist = distance;
			}
		}
		return closestjammer;
	}

	public static void jamNodeSometime(World world, BlockCoord node, int dimension, int freq)
	{
		jammednodes[dimension].put(node, -world.rand.nextInt(jammerblockwait));
	}

	public static void jamEntitySometime(EntityLiving entity)
	{
		jammedentities.put(entity, -entity.worldObj.rand.nextInt(jammerentitywait));
	}

	public static void jamNode(World world, BlockCoord node, int dimension, int freq)
	{		
		jammednodes[dimension].put(node, getRandomTimeout(world.rand));
	
		freqarray[freq].remTransmitter(world, node, dimension);
		freqarray[freq].remReceiver(world, node, dimension);
	}
	
	public static void jamNode(World world, int x, int y, int z, int freq)
	{
		jamNode(world, new BlockCoord(x, y, z), CoreProxy.getArrayDimension(world), freq);
	}

	public static void jamEntity(EntityLiving entity, boolean jam)
	{
		if(CoreProxy.isClient())//for the client hashing will only store player jams
		{
			if(jam)
			{
				jammedentities.put(entity, 1);
			}
			else
			{
				jammedentities.remove(entity);
			}
			return;
		}
		else
		{
			if(jam)//iterator.remove will be used to unjam entities. We only need to send the packet.
			{
				jammedentities.put(entity, getRandomTimeout(entity.worldObj.rand));
			}
			if(CoreProxy.isSMP() && entity instanceof EntityPlayer)
			{
				CorePacketHandler.sendJamPlayerPacketTo((EntityPlayer) entity, jam);
			}
		}
	}

	public static void jamNodesInAOEOfJammer(World world, BlockCoord jammer, int dimension)
	{
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			TreeMap<BlockCoord, Boolean> transmittermap = freqarray[freq].getTransmitters(dimension);
			for(Iterator<BlockCoord> iterator = transmittermap.keySet().iterator(); iterator.hasNext();)
			{
				BlockCoord node = iterator.next();
				if(pythagorasPow2(node, jammer) < jammerrangePow2)
				{
					jamNodeSometime(world, node, dimension, freq);
				}
			}
			
			TreeSet<BlockCoord> receiverset = freqarray[freq].getReceivers(dimension);
			for(Iterator<BlockCoord> iterator = receiverset.iterator(); iterator.hasNext();)
			{
				BlockCoord node = iterator.next();
				if(pythagorasPow2(node, jammer) < jammerrangePow2)
				{
					jamNodeSometime(world, node, dimension, freq);
				}
			}
		}
	}

	public static void unjamTile(World world, int x, int y, int z)
	{
		BlockCoord node = new BlockCoord(x, y, z);
		int dimension = CoreProxy.getArrayDimension(world);
		
		Integer timeout = jammednodes[dimension].remove(node);
		
		if(timeout != null && timeout >= 0)//tile was jammed
		{
			ITileWireless tile = (ITileWireless) getTile(world, node);
			tile.unjamTile();
		}
	}

	public static int getRandomTimeout(Random rand)
	{
		return jammertimeout + rand.nextInt(jammerrandom * 2) - jammerrandom;
	}

	public static boolean canBroadcastOnFrequency(EntityPlayer player, int freq)
	{
		if(!CoreProxy.isSMP() && !CoreProxy.isClient())
		{
			return true;
		}
		
		if(freq > numfreqs)
		{
			return false;
		}
	
		if(freq <= publicfrequencyend)
		{
			return true;
		}
		else
		{
			freq--;
			return (playerfrequencymap.get(player.username)[freq >> 3] & 1 << (freq & 7)) != 0;
		}
	}

	public static void HandleLogin(EntityPlayer player)
	{
		CorePacketHandler.sendResetClientTo(player);
		String openstring = SaveManager.getInstance(0).getProperty(player.username+".of", "");
		if(openstring != "")
		{
			loadOpenFrequencies(openstring, player);
		}
		else
		{
			jamAllFrequencies(player);
		}
		CorePacketHandler.sendPublicFrequencyTo(player, publicfrequencyend);
		freqinfoloadprogress.put(player.username, 0);
	}

	public static void saveOpenFrequencies(EntityPlayer player)
	{
		int endfreq = 0;
		StringBuilder openfreqs = new StringBuilder();
		do
		{
			int openrange[] = getNextOpenFrequencyRange(player, endfreq + 1);
			int startfreq = openrange[0];
			endfreq = openrange[1];
			if(startfreq == -1)
			{
				break;
			}
			if(openfreqs.length() != 0)
			{
				openfreqs.append(',');
			}
			if(startfreq == endfreq)
			{
				openfreqs.append(startfreq);
			}
			else
			{
				openfreqs.append(startfreq);
				openfreqs.append('-');
				openfreqs.append(endfreq);
			}
		}
		while(endfreq <= 5000);
		SaveManager.getInstance(0).setProperty(player.username+".of", openfreqs.toString());
	}

	public static void loadOpenFrequencies(String openstring, EntityPlayer player)
	{
		jamAllFrequencies(player);
		String freqranges[] = openstring.split(",");
		for(int i = 0; i < freqranges.length; i++)
		{
			String currentrange[] = freqranges[i].split("-");
			if(currentrange.length == 1)
			{
				int openfreq;
				try
				{
					openfreq = Integer.parseInt(currentrange[0]);
				}
				catch(NumberFormatException numberformatexception)
				{
					continue;
				}
				setFrequencyRange(player, openfreq, openfreq, false);
				continue;
			}
			int startfreq;
			int endfreq;
			try
			{
				startfreq = Integer.parseInt(currentrange[0]);
				endfreq = Integer.parseInt(currentrange[1]);
			}
			catch(NumberFormatException numberformatexception1)
			{
				continue;
			}
			setFrequencyRange(player, startfreq, endfreq, false);
		}
	}

	public static int[] getNextOpenFrequencyRange(EntityPlayer player, int beginfreq)
	{
		byte openfreq[] = playerfrequencymap.get(player.username);
		int currentfreq = beginfreq;
		int startfreq = -1;
		do
		{
			int arrayindex = (currentfreq-1) >> 3;
			int bit = (currentfreq-1) & 7;
			if(currentfreq > 5000)
			{
				if(startfreq != -1)
				{
					return (new int[]{startfreq, numfreqs});
				}
				else
				{
					return (new int[]{-1, -1});
				}
			}
			if(openfreq[arrayindex] == 0 && bit == 0)
			{
				if(startfreq != -1)
				{
					return (new int[]{startfreq, currentfreq-1});
				}
				currentfreq += 8;
			}
			else if(openfreq[arrayindex] == 0xFF && bit == 0)
			{
				if(startfreq == -1)
				{
					startfreq = currentfreq;
				}
				currentfreq += 8;
			}
			else if((openfreq[arrayindex] & 1 << bit) != 0)
			{
				if(startfreq == -1)
				{
					startfreq = currentfreq;
				}
				currentfreq++;
			}
			else
			{
				if(startfreq != -1)
				{
					return (new int[]{startfreq, currentfreq-1});
				}
				currentfreq++;
			}
		}
		while(true);
	}

	public static void jamAllFrequencies(EntityPlayer player)
	{
		if(CoreProxy.isSMP())
		{
			CorePacketHandler.sendJamAllFrequenciesTo(player);
		}
		playerfrequencymap.put(player.username, new byte[625]);
	}

	public static void setLastPublicFrequency(int pubfreqend)
	{
		publicfrequencyend = pubfreqend;
		if(CoreProxy.isSMP())
		{
			SaveManager.getInstance(0).setProperty("PublicFrequencies", pubfreqend);
			CorePacketHandler.sendPublicFrequencyTo(null, publicfrequencyend);
		}
	}
	
	public static int getLastPublicFrequency()
	{
		return publicfrequencyend;
	}
	
	public static void setFrequencyRange(EntityPlayer player, int firstfreq, int lastfreq, boolean jam)
	{
		if(CoreProxy.isSMP())
		{
			CorePacketHandler.sendSetFrequencyRangeTo(player, firstfreq, lastfreq, jam);
		}
		
		int settingfreq = firstfreq;
		int bit;
		int arrayid;
		
		byte[] openfreq = playerfrequencymap.get(player.username);
		
		while(settingfreq <= lastfreq)
		{
			arrayid = (settingfreq-1) >> 3;
			bit = (settingfreq-1) & 7;

			if(bit == 0 && settingfreq + 7 <= lastfreq)
			{
				if(jam)
				{
					openfreq[arrayid] = 0;
				}
				else
				{
					openfreq[arrayid] = (byte) 0xFF;
				}
				settingfreq += 8;
			}
			else
			{
				if(jam)
				{
					openfreq[arrayid] &= ((1 << bit) ^ 0xFF);// set bit to 0
				}
				else
				{
					openfreq[arrayid] |= (1 << bit);// set bit 1
				}
				settingfreq++;
			}
		}
	}
	
	public static void setFrequencyRangeCommand(EntityPlayer player, int startfreq, int endfreq, boolean flag)
	{
		setFrequencyRange(player, startfreq, endfreq, flag);
		saveOpenFrequencies(player);
	}
	
	public static int[] parseFrequencyRange(String freqstring)
	{
		String splitstring[] = freqstring.split("-");
		if(splitstring.length == 1)
		{
			try
			{
				return (new int[]{Integer.parseInt(splitstring[0]), Integer.parseInt(splitstring[0])});
			}
			catch(NumberFormatException numberformatexception)
			{
				return (new int[]{-1, -1});
			}
		}
		if(splitstring.length == 2)
		{
			try
			{
				return (new int[]{Integer.parseInt(splitstring[0]), Integer.parseInt(splitstring[1])});
			}
			catch(NumberFormatException numberformatexception1)
			{
				return (new int[]{-1, -1});
			}
		}
		else
		{
			return (new int[]{-1, -1});
		}
	}
	
	public static void registerColourSetters(ItemStack[] colours)
	{
		if(coloursetters == null)
		{
			if(colours.length == numcolours + 1)
			{
				coloursetters = colours;
			}
			else
			{
				ModLoader.ThrowException("Wireless Redstone", new Exception("Not "+(numcolours + 1)+" colours in setter!"));
			}
		}
		else
		{
			ModLoader.ThrowException("Wireless Redstone", new Exception("Colour Setters Already Set"));
		}
	}

	public static ItemStack[] getColourSetters()
	{
		if(coloursetters == null)
		{
			coloursetters = new ItemStack[]{
				new ItemStack(Item.dyePowder, 1, 1),
				new ItemStack(Item.dyePowder, 1, 2),
				new ItemStack(Item.dyePowder, 1, 3),
				new ItemStack(Item.dyePowder, 1, 4),
				new ItemStack(Item.dyePowder, 1, 5),
				new ItemStack(Item.dyePowder, 1, 6),
				new ItemStack(Item.dyePowder, 1, 7),
				new ItemStack(Item.dyePowder, 1, 8),
				new ItemStack(Item.dyePowder, 1, 9),
				new ItemStack(Item.dyePowder, 1, 10),
				new ItemStack(Item.dyePowder, 1, 11),
				new ItemStack(Item.dyePowder, 1, 12),
				new ItemStack(Item.dyePowder, 1, 13),
				new ItemStack(Item.dyePowder, 1, 14),
				new ItemStack(Item.redstone, 1)};
		}
		return coloursetters;
	}

	private static void sendFreqInfo()
	{
		for(Iterator<String> iterator = freqinfoloadprogress.keySet().iterator(); iterator.hasNext();)
		{
			EntityPlayer player = CoreProxy.getPlayer(iterator.next());
			int infosent = 0;
			int lastsentfreq = freqinfoloadprogress.get(player.username);
			int freq = lastsentfreq + 1;
			while(infosent < 10 && freq <= numfreqs)
			{
				String freqname = freqarray[freq].getName();
				int colourid = freqarray[freq].getColourId();
				if(!freqname.equals("") || colourid != -1)
				{
					CorePacketHandler.sendSetFreqInfoTo(player, freq, freqname, colourid);
					infosent++;
				}
				freq++;
			}
			if(freq == numfreqs + 1)
			{
				iterator.remove();
			}
			else
			{
				freqinfoloadprogress.put(player.username, freq);
			}
		}
	}

	public static int getFreqColour(int freq)
	{
		int id = getFreqColourId(freq);
		if(id == -1)
		{
			return 0xFFFFFFFF;
		}
		else
		{
			return colours[id];
		}
	}
	
	public static int getFreqColourId(int freq)
	{
		if(freq == 0)
		{
			return -1;
		}
		return freqarray[freq].getColourId();
	}
	
	public static String getFreqColourName(int freq, boolean local)
	{
		int id = getFreqColourId(freq);
		
		return local ? localdyenames[id] : fulldyenames[id];
	}
	
	public static void setFreqColour(int freq, int colourid)
	{
		freqarray[freq].setColour(colourid);
	}
	
	public static String getFreqName(int freq)
	{
		if(freq == 0)
		{
			return null;
		}
		return freqarray[freq].getName();
	}
	
	public static void setFreqName(int freq, String name)
	{
		freqarray[freq].setName(name);
	}
	
	public static void setFreqClean(int freq, int dimension)
	{
		freqarray[freq].setClean(dimension);
	}
	
	public static ArrayList<String> getMatchingAllowedNames(EntityPlayer player, String match)
	{
		ArrayList<String> allnames = new ArrayList<String>();
		
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			String name = freqarray[freq].getName();
			if(name == null || name.equals("") || !canBroadcastOnFrequency(player, freq) ||
					name.length() < match.length() || !name.substring(0, match.length()).equalsIgnoreCase(match))
			{
				continue;
			}
			allnames.add(name);
		}
		
		return allnames;
	}
	
	public static ArrayList<String> getAllowedNames(EntityPlayer player)
	{
		ArrayList<String> allnames = new ArrayList<String>();
		
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			String name = freqarray[freq].getName();
			if(name == null || name.equals("") || !canBroadcastOnFrequency(player, freq))
			{
				continue;
			}
			allnames.add(name);
		}
		
		return allnames;
	}
	
	public static ArrayList<String> getAllNames()
	{
		ArrayList<String> allnames = new ArrayList<String>();
		
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			String name = freqarray[freq].getName();
			if(name == null || name.equals(""))
			{
				continue;
			}
			allnames.add(name);
		}
		
		return allnames;
	}
	
	public static int getFreqByName(String slotname)
	{
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			String name = freqarray[freq].getName();
			if(name != null && name.equals(slotname))
			{
				return freq;
			}
		}
		return -1;
	}
	
	public static TreeMap<Integer, Integer> getLoadedFrequencies()
	{
		TreeMap<Integer, Integer> treemap = new TreeMap<Integer, Integer>();
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			if(freqarray[freq].nodeCount() != 0)
			{
				treemap.put(freq, freqarray[freq].getActiveTransmitters());
			}
		}
	
		return treemap;
	}

	public static Map<BlockCoord, Boolean> getTransmittersOnFreq(int freq, int dimension)
	{
		return Collections.unmodifiableMap(freqarray[freq].getTransmitters(dimension));
	}

	public static Collection<BlockCoord> getReceiversOnFreq(int freq, int dimension)
	{
		return Collections.unmodifiableCollection(freqarray[freq].getReceivers(dimension));
	}

	public static ArrayList<FreqBlockCoord> getActiveTransmitters(int dimension)
	{
		ArrayList<FreqBlockCoord> txnodes = new ArrayList<FreqBlockCoord>();
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			freqarray[freq].putActiveTransmittersInList(dimension, txnodes);
		}
		return txnodes;
	}

	public static ArrayList<FreqBlockCoord> getActiveTransmittersOnFreq(int freq, int dimension)
	{
		ArrayList<FreqBlockCoord> txnodes = new ArrayList<FreqBlockCoord>();
		freqarray[freq].putActiveTransmittersInList(dimension, txnodes);
		return txnodes;
	}
	
	public static TreeSet<BlockCoord> getJammers(int dimension)
	{
		return jammerset[dimension];
	}

	public static TreeMap<BlockCoord, Integer> getJammedNodes(int dimension)
	{
		return jammednodes[dimension];
	}
	
	public static TreeSet<BlockCoord> getNodesInRangeofPoint(int dimension, WRVector3 point, float range, boolean includejammed)
	{
		TreeSet<BlockCoord> nodes = new TreeSet<BlockCoord>();
		float rangePow2 = range*range;
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			TreeMap<BlockCoord, Boolean> transmittermap = freqarray[freq].getTransmitters(dimension);
			for(Iterator<BlockCoord> iterator = transmittermap.keySet().iterator(); iterator.hasNext();)
			{
				BlockCoord node = iterator.next();
				if(pythagorasPow2(node, point) < rangePow2)
				{
					nodes.add(node);
				}
			}
			
			TreeSet<BlockCoord> receiverset = freqarray[freq].getReceivers(dimension);
			for(Iterator<BlockCoord> iterator = receiverset.iterator(); iterator.hasNext();)
			{
				BlockCoord node = iterator.next();
				if(pythagorasPow2(node, point) < rangePow2)
				{
					nodes.add(node);
				}
			}
		}
		
		if(includejammed)
		{
			for(Iterator<BlockCoord> iterator = jammednodes[dimension].keySet().iterator(); iterator.hasNext();)
			{
				BlockCoord node = iterator.next();
				if(pythagorasPow2(node, point) < rangePow2)
				{
					nodes.add(node);
				}
			}
		}
		
		return nodes;
	}
	
	public static TreeSet<BlockCoord> getNodesInRangeofNode(int dimension, BlockCoord block, float range, boolean includejammed)
	{
		TreeSet<BlockCoord> nodes = new TreeSet<BlockCoord>();
		float rangePow2 = range*range;
		for(int freq = 1; freq <= numfreqs; freq++)
		{
			TreeMap<BlockCoord, Boolean> transmittermap = freqarray[freq].getTransmitters(dimension);
			for(Iterator<BlockCoord> iterator = transmittermap.keySet().iterator(); iterator.hasNext();)
			{
				BlockCoord node = iterator.next();
				if(pythagorasPow2(node, block) < rangePow2)
				{
					nodes.add(node);
				}
			}
			
			TreeSet<BlockCoord> receiverset = freqarray[freq].getReceivers(dimension);
			for(Iterator<BlockCoord> iterator = receiverset.iterator(); iterator.hasNext();)
			{
				BlockCoord node = iterator.next();
				if(pythagorasPow2(node, block) < rangePow2)
				{
					nodes.add(node);
				}
			}
		}

		if(includejammed)
		{
			for(Iterator<BlockCoord> iterator = jammednodes[dimension].keySet().iterator(); iterator.hasNext();)
			{
				BlockCoord node = iterator.next();
				if(pythagorasPow2(node, block) < rangePow2)
				{
					nodes.add(node);
				}
			}
		}
		
		return nodes;
	}
	
	public static float getRotation(long worldTime, int freq, float partialtime)
	{
		if(freq == 0)
		{
			return 0;
		}
		int time = (int) (worldTime % 100000);
		
		float spinangle = (float) (((time + partialtime) / 20F) * (gradrps * freq + minrps));
		return spinangle * 6.2832F;
	}
	
	public static void updateReceivingDevices(int freq, boolean on)
	{
		for(Iterator<WirelessReceivingDevice> iterator = receivingdevices.iterator(); iterator.hasNext();)
		{
			iterator.next().updateDevice(freq, on);
		}
	}
	
	private static RedstoneEtherFrequency[] freqarray;

	private static int publicfrequencyend;
	
	private static HashSet<WirelessReceivingDevice> receivingdevices;
	
	private static HashMap<String, byte[]> playerfrequencymap;
	private static HashMap<String, Integer> freqinfoloadprogress;
	
	private static int processingAddittions = -1;
	@SuppressWarnings("unchecked")
	private static HashMap<BlockCoord, Integer>[] wirelessloadlist = new HashMap[2];
	private static HashMap<BlockCoord, Integer>backupmap;
	
	@SuppressWarnings("unchecked")
	private static TreeSet<BlockCoord> jammerset[] = new TreeSet[2];
	@SuppressWarnings("unchecked")
	private static TreeMap<BlockCoord, Integer> jammednodes[] = new TreeMap[2];
	private static HashMap<EntityLiving, Integer> jammedentities;

	public static final int numfreqs = 5000;
	
	public static final int jammerrange = SaveManager.config().getTag("core.jammer.range").setComment("Range In Blocks").getIntValue(10);
	public static final int jammerrangePow2 = jammerrange * jammerrange;
	public static final int jammertimeout = SaveManager.config().getTag("core.jammer.timeout").setComment("Timeout In Seconds:Applies to both blocks and players").getIntValue(60) * 20;
	public static final int jammerrandom = jammertimeout / 3;
	public static final int jammerentitywait = SaveManager.config().getTag("core.jammer.entitydelay").getIntValue(5) * 20;
	public static final int jammerentityretry = SaveManager.config().getTag("core.jammer.entityretry").setComment("Jam an entity again after x seconds").getIntValue(10) * 20;
	public static final int jammerblockwait = SaveManager.config().getTag("core.jammer.blockdelay").setComment("Delay in seconds before jamming the first time").getIntValue(10) * 20;

	public static ItemStack[] coloursetters;

	public static final int numcolours = 14;
	public static final float minrps = 0.02F;
	public static final float maxrps = 2.98F;
	public static final double gradrps = maxrps / 5000D;
	
	public static final String localdyenames[] = {
			"red", "green", "brown", "blue", "purple", "cyan", "silver", "gray", 
			"pink", "lime", "yellow", "lightBlue", "magenta", "orange"};
	
	public static final String fulldyenames[] = {
			"Red", "Green", "Brown", "Blue", "Purple", "Cyan", "Light Gray", "Gray", 
			"Pink", "Lime", "Yellow", "Light Blue", "Magenta", "Orange"};
	
	public static final int colours[] = {
			0xFFB3312C, 0xFF336600, 0xFF51301A, 0xFF253192, 0xFF7B2FBE, 0xFF287697, 0xFF848484, 0xFF434343, 0xFFD88198, 
	        0xFF41CD34, 0xFFDECF2A, 0xFF6689D3, 0xFFC354CD, 0xFFEB8844};

	public static List<WirelessTransmittingDevice> getTransmittingDevicesOnFreq(int freq)
	{
		return Collections.unmodifiableList(freqarray[freq].getTransmittingDevices());
	}
	
	public static void addTransmittingDevice(WirelessTransmittingDevice device)
	{
		freqarray[device.getFreq()].addTransmittingDevice(device);
	}
	
	public static void removeTransmittingDevice(WirelessTransmittingDevice device)
	{
		freqarray[device.getFreq()].removeTransmittingDevice(device);
	}

	public static void addReceivingDevice(WirelessReceivingDevice device)
	{
		receivingdevices.add(device);
	}
	
	public static void removeReceivingDevice(WirelessReceivingDevice device)
	{
		receivingdevices.remove(device);
	}
}